package com.allin.mall.common.redis.service.impl;

import com.allin.mall.common.core.api.ResultCodeEnum;
import com.allin.mall.common.core.exception.BusinessException;
import com.allin.mall.common.redis.service.RedisDistributedLock;
import jakarta.annotation.Resource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;


public class RedisDistributedLockImpl implements RedisDistributedLock {

    @Resource
    private RedisScript<Long> lockScript;

    @Resource
    private RedisScript<Long> unlockScript;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    private static final ThreadLocal<String> lockValueHolder = new ThreadLocal<>();

    @Override
    public void lock(String key, TimeUnit unit, long time) {
        while (!tryLock(key, unit, time)) {
            try {
                TimeUnit.MILLISECONDS.sleep(50);
            } catch (InterruptedException e) {
                // ig
            }
        }
    }

    @Override
    public boolean unlock(String key) {
        String lockValue = lockValueHolder.get();
        if (lockValue == null) {
            throw new BusinessException(ResultCodeEnum.SYSTEM_ERROR);
        }
        try {
            Long result = redisTemplate.execute(
                    unlockScript,
                    Collections.singletonList(key),
                    lockValue
            );
            return result == 1;
        } finally {
            lockValueHolder.remove();
        }
    }

    @Override
    public boolean tryLock(String key, TimeUnit unit, long time) {
        String lockValue = UUID.randomUUID().toString();
        Long result = redisTemplate.execute(
                lockScript,
                Collections.singletonList(key),
                lockValue,
                unit.toMillis(time)
        );
        boolean success = result == 1;
        if (success) {
            lockValueHolder.set(lockValue);
        }
        return success;
    }
}
